<!--
 * @Description: 图片旋转缩放组件
 * @Author: yeke
 * @Date: 2023-10-01 14:35:16
 * @LastEditors: yeke
 * @LastEditTime: 2024-01-13 12:23:11
-->
<template>
  <view class="img-container" id="img-container">
    <view v-if="showMask" class="mask"></view>
    <view
      class="close-btn"
      :style="{ top: closeTopStyle }"
      @click.stop="handelClose"
    >
      <view class="close-icon" v-html="closeIcon"></view>
    </view>
    <!-- 左右切换 -->
    <template v-if="showTab">
      <view
        class="trigger trigger-left"
        @click.stop="handelLast"
        v-html="backIcon"
      >
      </view>
      <view
        class="trigger trigger-right"
        @click.stop="handelNext"
        v-html="nextIcon"
      >
      </view>
    </template>

    <!-- 底部操作栏 -->
    <view v-if="showOperation" class="operation-box">
      <view
        v-for="(item, index) in operationList"
        :key="index"
        class="item"
        @click="handelOperation(item)"
      >
        <view v-html="item.icon"></view>
      </view>
    </view>

    <view
      class="img-box"
      @touchstart="handleTouchStart"
      @touchmove="handleTouchMove"
      @touchend="handleTouchEnd"
      @click.stop="handelClose"
    >
      <view v-if="count > 1" class="count" :style="{ top: countTopStyle }">
        {{ activeIndex + 1 }} / {{ count }}
      </view>
      <template v-for="(item, index) in imgList">
        <image
          v-if="!judgeImageOver"
          :key="index + new Date().getTime()"
          class="img-style-placeholder"
          :style="{
            width: newImgStyleWidth,
            height: newImgStyleHeight,
            top: newImgStyle.top + 'px',
            left: newImgStyle.left + 'px',
            transform: newImgStyle.transform,
            transition: newImgStyle.transition,
          }"
        >
        </image>
        <image
          v-if="
            activeIndex === index &&
            imageResult &&
            newImgStyle &&
            judgeImageOver
          "
          :key="index"
          class="img-style"
          :style="{
            width: newImgStyleWidth,
            height: newImgStyleHeight,
            top: newImgStyle.top + 'px',
            left: newImgStyle.left + 'px',
            transform: newImgStyle.transform,
            transition: newImgStyle.transition,
          }"
          @load="loadImage"
          @error="errorImage"
          :src="item"
        >
        </image>
      </template>

      <image v-if="!imageResult" class="img-style" :src="errImage"> </image>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { PreviewImage } from "./util";
import type { ImageStyle } from "./util";
enum OperationEnum {
  LEFT = "left",
  RIGHT = "right",
  LAST = "last",
  NEXT = "next",
}

interface OperationListItem {
  label: string;
  icon: string;
  key: OperationEnum;
}
const props = withDefaults(
  defineProps<{
    url: string | string[];
    index?: number;
    show?: boolean;
    isTouchDeg?: boolean;
    showOperation?: boolean;
    countTop?: string;
    closeTop?: string;
  }>(),
  {
    url: "",
    index: -1,
    show: false,
    isTouchDeg: false,
    showOperation: true,
    countTop: "30",
    closeTop: "30",
  }
);

const emit = defineEmits<{
  (e: "close", value: boolean): void;
}>();

const count = computed(() => {
  return Array.isArray(props.url) ? props.url.length : 1;
});

const countTopStyle = computed(() => props.countTop + "rpx");
const closeTopStyle = computed(() => props.closeTop + "rpx");

const activeIndex = ref(props.index);
const previewImage = ref<PreviewImage>(new PreviewImage());
const imgSrc = ref();
const imgList = ref<string[]>();
const errImage = ref();
const imageResult = ref(true);
const showMask = ref(true);
const newImgStyle = ref<ImageStyle>(previewImage.value.getImgStyle());
const newImgStyleWidth = computed(() =>
  typeof newImgStyle.value.width === "string"
    ? newImgStyle.value.width
    : newImgStyle.value.width + "px"
);
const newImgStyleHeight = computed(() =>
  typeof newImgStyle.value.height === "string"
    ? newImgStyle.value.height
    : newImgStyle.value.height + "px"
);
const judgeImageOver = computed(() => previewImage.value.getJudgeImageOver());

const closeIcon = computed(() => {
  return `<svg t="1704534217970" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4016" width="20" height="20"><path d="M571.733333 512l268.8-268.8c17.066667-17.066667 17.066667-42.666667 0-59.733333-17.066667-17.066667-42.666667-17.066667-59.733333 0L512 452.266667 243.2 183.466667c-17.066667-17.066667-42.666667-17.066667-59.733333 0-17.066667 17.066667-17.066667 42.666667 0 59.733333L452.266667 512 183.466667 780.8c-17.066667 17.066667-17.066667 42.666667 0 59.733333 8.533333 8.533333 19.2 12.8 29.866666 12.8s21.333333-4.266667 29.866667-12.8L512 571.733333l268.8 268.8c8.533333 8.533333 19.2 12.8 29.866667 12.8s21.333333-4.266667 29.866666-12.8c17.066667-17.066667 17.066667-42.666667 0-59.733333L571.733333 512z" fill="#ffffff" p-id="4017"></path></svg>`;
});
const backIcon = computed(() => {
  return `<svg t="1703396047580" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5936" width="20" height="20"><path d="M729.6 931.2l-416-425.6 416-416c9.6-9.6 9.6-25.6 0-35.2-9.6-9.6-25.6-9.6-35.2 0l-432 435.2c-9.6 9.6-9.6 25.6 0 35.2l432 441.6c9.6 9.6 25.6 9.6 35.2 0C739.2 956.8 739.2 940.8 729.6 931.2z" p-id="5937" fill="#ffffff"></path></svg>`;
});
const nextIcon = computed(() => {
  return `<svg t="1703395846439" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5782" width="20" height="20"><path d="M761.6 489.6l-432-435.2c-9.6-9.6-25.6-9.6-35.2 0-9.6 9.6-9.6 25.6 0 35.2l416 416-416 425.6c-9.6 9.6-9.6 25.6 0 35.2s25.6 9.6 35.2 0l432-441.6C771.2 515.2 771.2 499.2 761.6 489.6z" p-id="5783" fill="#ffffff"></path></svg>`;
});
const leftIcon = computed(() => {
  return `<svg t="1616925730136" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1951" width="25" height="25"><path d="M503.466667 285.866667L405.333333 226.133333c-8.533333-8.533333-12.8-21.333333-8.533333-29.866666 8.533333-8.533333 21.333333-12.8 29.866667-8.533334l145.066666 89.6c8.533333 4.266667 12.8 17.066667 8.533334 29.866667l-89.6 145.066667c-4.266667 8.533333-17.066667 12.8-29.866667 8.533333-8.533333-4.266667-12.8-17.066667-8.533333-29.866667l64-102.4c-123.733333 4.266667-226.133333 106.666667-226.133334 234.666667s106.666667 234.666667 234.666667 234.666667c85.333333 0 162.133333-46.933333 204.8-119.466667 4.266667-8.533333 17.066667-12.8 29.866667-8.533333 8.533333 4.266667 12.8 17.066667 8.533333 29.866666-51.2 85.333333-140.8 140.8-238.933333 140.8-153.6 0-277.333333-123.733333-277.333334-277.333333 0-145.066667 110.933333-264.533333 251.733334-277.333333z" p-id="1952" fill="#ffffff"></path></svg>`;
});
const rightIcon = computed(() => {
  return `<svg t="1616925759280" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2144" width="25" height="25"><path d="M520.533333 285.866667c140.8 12.8 251.733333 132.266667 251.733334 277.333333 0 153.6-123.733333 277.333333-277.333334 277.333333-98.133333 0-192-55.466667-238.933333-140.8-4.266667-8.533333-4.266667-21.333333 8.533333-29.866666 8.533333-4.266667 21.333333-4.266667 29.866667 8.533333 42.666667 72.533333 119.466667 119.466667 204.8 119.466667 128 0 234.666667-106.666667 234.666667-234.666667s-98.133333-230.4-226.133334-234.666667l64 102.4c4.266667 8.533333 4.266667 21.333333-8.533333 29.866667-8.533333 4.266667-21.333333 4.266667-29.866667-8.533333l-89.6-145.066667c-4.266667-8.533333-4.266667-21.333333 8.533334-29.866667L597.333333 187.733333c8.533333-4.266667 21.333333-4.266667 29.866667 8.533334 4.266667 8.533333 4.266667 21.333333-8.533333 29.866666l-98.133334 59.733334z" p-id="2145" fill="#ffffff"></path></svg>`;
});

const showTab = computed(() => {
  return props.url.length > 1;
});

const operationList = ref<OperationListItem[]>([
  {
    label: "左旋",
    icon: leftIcon.value,
    key: OperationEnum.LEFT,
  },
  {
    label: "右旋",
    icon: rightIcon.value,
    key: OperationEnum.RIGHT,
  },
]);

onMounted(() => {
  hasUrl();
  setImageSrc();
  uni.showLoading({
    title: "加载中",
    // mask: true,
  });
  showMask.value = true;
  initPreviewImage();
});

const initPreviewImage = () => {
  previewImage.value.init({
    containerId: "#img-container",
    imgUrl: imgSrc.value,
    degIf: props.isTouchDeg,
    deg: 0,
    scale: 1,
    minScale: 0.5,
    maxScale: 20,
    judgeImageOver: false,
  });
};

const handleTouchStart = (e: TouchEvent) => {
  previewImage.value.handleTouchStart(e);
};
const handleTouchMove = (e: TouchEvent) => {
  previewImage.value.handleTouchMove(e);
};
const handleTouchEnd = (e: TouchEvent) => {
  previewImage.value.handleTouchEnd(e);
};

const handelOperation = (data: OperationListItem) => {
  console.log(data);
  switch (data.key) {
    case OperationEnum.LEFT:
      previewImage.value.rotateRight();
      break;
    case OperationEnum.RIGHT:
      previewImage.value.rotateLeft();
      break;

    default:
      break;
  }
};

const handelLast = () => {
  imageResult.value = true;
  if (activeIndex.value === 0) {
    uni.showToast({
      icon: "none",
      title: "已是第一张",
    });
    return;
  }
  showMask.value = true;
  uni.showLoading({
    title: "加载中",
    // mask: true,
  });
  --activeIndex.value;
  setImageSrc();
  initPreviewImage();
};

const handelNext = () => {
  imageResult.value = true;
  if (activeIndex.value === props.url.length - 1) {
    uni.showToast({
      icon: "none",
      title: "已是最后一张",
    });
    return;
  }
  showMask.value = true;
  uni.showLoading({
    title: "加载中",
    // mask: true,
  });
  ++activeIndex.value;
  console.log(activeIndex.value);
  setImageSrc();
  initPreviewImage();
};

/**
 * @description: 判断props是否正确
 */
const hasUrl = () => {
  if (!(props.url && props.url !== "") || !props.url.length) {
    uni.showToast({
      icon: "error",
      title: "图片不能为空",
    });
    handelClose();
    return;
  }
};

/**
 * @description: 判断url是否是数组
 */
const setImageSrc = () => {
  if (Array.isArray(props.url)) {
    imgSrc.value = props.url[activeIndex.value];
    imgList.value = props.url;
  } else {
    imgSrc.value = props.url;
    imgList.value = [props.url];
  }
};

const handelClose = () => {
  emit("close", false);
};

const loadImage = () => {
  console.log(imgSrc.value);
  uni.hideLoading();
  showMask.value = false;
};

const errorImage = () => {
  imageResult.value = false;
  uni.hideLoading();
  showMask.value = false;
};
</script>

<style scoped lang="scss">
.img-container {
  width: 100%;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1001;
  .mask {
    width: 100%;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 9999;
    background-color: transparent;
  }
  .close-btn {
    position: fixed;
    right: 30rpx;
    z-index: 10001;
  }
  .count {
    position: absolute;
    left: calc(50% - 70rpx);
    z-index: 1003;
    width: 140rpx;
    height: 40rpx;
    line-height: 42rpx;
    text-align: center;
    color: #fff;
    background-color: rgba($color: #4e4e4e, $alpha: 0.8);
    border-radius: 50rpx;
  }
  .trigger {
    color: #fff;
    width: 80rpx;
    height: 80rpx;
    background-color: rgba($color: #4e4e4e, $alpha: 0.8);
    border-radius: 100rpx;
    overflow: hidden;
    position: absolute;
    top: calc(50% - 35rpx);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 1003;
  }
  .trigger-left {
    left: 20rpx;
  }
  .trigger-right {
    right: 20rpx;
  }
  .operation-box {
    color: #fff;
    background-color: rgba($color: #4e4e4e, $alpha: 0.8);
    border-radius: 70rpx;
    padding: 0 30rpx;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    left: calc(50% - 115rpx);
    bottom: 30rpx;
    z-index: 1003;
    .item {
      margin: 10rpx 20rpx;
    }
  }
  .img-box {
    width: 100%;
    height: 100vh;
    background: rgba(0, 0, 0, 1);
    transition: all 0.5s;
    display: flex;
    justify-content: center;
    align-items: center;
    .img-style {
      background: rgba(0, 0, 0, 1);
      position: absolute;
      z-index: 1002;
    }
    .img-style-placeholder {
      background: rgba(0, 0, 0, 1);
      opacity: 0 !important;
      position: absolute;
      z-index: 1002;
    }
  }

  .slot-loading {
    width: 100%;
    height: 100%;
    color: #fff;
    background-color: rgba(0, 0, 0, 1);
    font-size: 28rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding-top: 150rpx;
    box-sizing: border-box;
    .label {
      margin-top: 20rpx;
    }
  }
  .slot-error {
    color: #fff;
    background-color: rgba(0, 0, 0, 1);
    font-size: 28rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    .label {
      margin-top: 20rpx;
    }
  }
}
</style>
